package grammar.parser.stmt;

import error.processing.MyErrorProcessing;
import execution.InOutProcess;
import exp.parser.Exp;
import word.parser.WordElement;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PrintfStmt extends Stmt {
    private final String formatString;
    private final List<Exp> exps;

    public PrintfStmt(String formatString, List<Exp> exps) {
        this.formatString = formatString;
        this.exps = exps;
    }

    public static PrintfStmt buildPrintStmt() {
        assert getAnalyzingWord().getType() == WordElement.WordType.PRINTFTK;
        int positionOfPrintf = getNowPosition();
        analyzeNextWord();

        assert getAnalyzingWord().getType() == WordElement.WordType.LPARENT;
        analyzeNextWord();
        assert getAnalyzingWord().getType() == WordElement.WordType.STRCON;
        String formatString = getAnalyzingWord().getWord();
        int numOfPlaceholder = getNumOfPlaceholder(formatString);

        List<Exp> exps = new ArrayList<>();
        analyzeNextWord();
        while (getAnalyzingWord().getType() == WordElement.WordType.COMMA) {
            analyzeNextWord();
            exps.add(Exp.buildExp());
        }
        if (exps.size() != numOfPlaceholder) {
            MyErrorProcessing.buildErrorProcessing(MyErrorProcessing.ErrorType.l,
                    getWord(positionOfPrintf));
        }

        if (getAnalyzingWord().getType() != WordElement.WordType.RPARENT) {
            MyErrorProcessing.buildErrorProcessing(MyErrorProcessing.ErrorType.j,
                    getAnalyzingWord(-1));
        } else {
            analyzeNextWord();
        }
        if (getAnalyzingWord().getType() != WordElement.WordType.SEMICN) {
            MyErrorProcessing.buildErrorProcessing(MyErrorProcessing.ErrorType.i, getAnalyzingWord(-1));
        } else {
            analyzeNextWord();
        }
        return new PrintfStmt(formatString, exps);
    }

    private static int getNumOfPlaceholder(String formatString) {
        char last = '\0';
        int placeHolder = 0;
        String stringWithoutDoubleQuotes = formatString.substring(1, formatString.length() - 1);
        boolean wrongFlag = false;
        for (char a : stringWithoutDoubleQuotes.toCharArray()) {
            if (!charLegalityTest(a)) {
                if (a != '\\' && a != '%') {
                    wrongFlag = true;
                }
            }

            if (last == '\\') {
                if (a != 'n') {
                    wrongFlag = true;
                }
            }
            if (last == '%') {
                if (a == 'd') {
                    placeHolder++;
                } else {
                    wrongFlag = true;
                }
            }
            last = a;
        }

        if (wrongFlag) {
            MyErrorProcessing.buildErrorProcessing(MyErrorProcessing.ErrorType.a,
                    getAnalyzingWord());
        }
        return placeHolder;
    }

    private static boolean charLegalityTest(char a) {
        int x = (int) a;
        return x == 32 || x == 33 || (x >= 40 && x <= 126);
    }

    @Override
    public void execute() {
        String ans = getFormatString();
        InOutProcess.printf(ans);
    }

    private String getFormatString() {
        Pattern placeHolderPattern = Pattern.compile("([^%]*)%d(.*)");
        Pattern nextLinePattern = Pattern.compile("([^\\\\]*)\\\\n(.*)");
        String stringWithoutDoubleQuotes = formatString.substring(1, formatString.length() - 1);
        String ans = "";
        String last = stringWithoutDoubleQuotes;
        int cnt = 0;
        while (!last.isEmpty()) {
            Matcher placeHolderMatcher = placeHolderPattern.matcher(last);
            if (placeHolderMatcher.find()) {
                ans = ans.concat(placeHolderMatcher.group(1));
                ans = ans.concat(exps.get(cnt).getValue().toString());
                last = placeHolderMatcher.group(2);
                cnt++;
            } else {
                ans = ans.concat(last);
                break;
            }
        }
        last = ans;
        ans = "";
        while (!last.isEmpty()) {
            Matcher nextLineMatcher = nextLinePattern.matcher(last);
            if (nextLineMatcher.find()) {
                ans = ans.concat(nextLineMatcher.group(1));
                ans = ans.concat("\n");
                last = nextLineMatcher.group(2);
                cnt++;
            } else {
                ans = ans.concat(last);
                break;
            }
        }
        return ans;
    }

}
